package com.wiscamp.ninechapters.problems.application.services;

import com.wiscamp.ninechapters.common.exception.ServiceException;
import com.wiscamp.ninechapters.problems.application.assemblers.MistakeAssembler;
import com.wiscamp.ninechapters.problems.application.assemblers.TaskReportAssembler;
import com.wiscamp.ninechapters.problems.application.dto.commands.CreateMistakeCommand;
import com.wiscamp.ninechapters.problems.application.dto.commands.mistakes.CreateMistakesByReportCommand;
import com.wiscamp.ninechapters.problems.application.dto.commands.mistakes.MistakeQuantityBySetTypeDto;
import com.wiscamp.ninechapters.problems.application.dto.vo.mistakes.MistakeQuestionVO;
import com.wiscamp.ninechapters.problems.application.dto.vo.sheets.MistakeVO;
import com.wiscamp.ninechapters.problems.domain.models.mistakes.Mistake;
import com.wiscamp.ninechapters.problems.domain.repositories.MistakeRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.SheetRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.TaskReportRepository;
import com.wiscamp.ninechapters.problems.domain.repositories.TaskRepository;
import com.wiscamp.ninechapters.problems.domain.services.MistakeService;
import com.wiscamp.ninechapters.problems.domain.services.SheetService;
import com.wiscamp.ninechapters.problems.domain.services.TaskReportService;
import cube.common.core.CollectionUtils;
import cube.ddd.application.BaseAppService;
import cube.ddd.application.command.DeleteCommand;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.common.query.PagedObjects;
import cube.ddd.common.query.PagedQuery;
import cube.ddd.utils.AppServiceUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Objects;

@Service
public class MistakeAppService extends BaseAppService {

    private final TaskRepository taskRepository;

    private final SheetRepository sheetRepository;

    private final MistakeRepository mistakeRepository;

    private final MistakeService mistakeService;

    private final TaskReportService taskReportService;

    public MistakeAppService(TaskRepository taskRepository, SheetRepository sheetRepository, TaskReportRepository taskReportRepository, SheetService sheetService, MistakeRepository mistakeRepository, MistakeService mistakeService, TaskReportService taskReportService) {
        this.taskRepository = taskRepository;
        this.sheetRepository = sheetRepository;
        this.mistakeRepository = mistakeRepository;
        this.mistakeService = mistakeService;
        this.taskReportService = taskReportService;
    }


    @HandleServiceException
    public PagedObjects<MistakeVO> getMistakesByQuery(@NotNull PagedQuery query) {
        var pagedEntity = mistakeRepository.getMistakesByQuery(query);
        return AppServiceUtils.convertPagedObjects(pagedEntity, MistakeAssembler.Instance::toMistakeVO);
    }

    @HandleServiceException
    public Collection<MistakeVO> getMistakesByUser(long userId) {
        var mistakes = mistakeRepository.getMistakesByUser(userId);
        return CollectionUtils.toArrayList(mistakes, MistakeAssembler.Instance::toMistakeVO);
    }

    @HandleServiceException
    public Collection<MistakeQuantityBySetTypeDto> getGroupedMistakesQuantityBySetType(long userId) {
        var mistakeQuantity = mistakeRepository.getGroupedMistakesQuantityBySetType(userId);
        return CollectionUtils.toArrayList(mistakeQuantity,
                MistakeAssembler.Instance::toMistakeQuantityBySetTypeDto);
    }

    @HandleServiceException
    public MistakeQuestionVO getMistakeQuestion(@Min(0) long mistakeId) {
        var mistake = mistakeService.getMistake(mistakeId);
        if (Objects.isNull(mistake)) return null;
        var report = taskReportService.getReportByTask(mistake.getTaskId());
        var reportProblem = report.getReportProblems().stream().filter(rp -> rp.getProblemId() == mistake.getProblemId())
                .findFirst().orElse(null);
        if (Objects.isNull(reportProblem)) return null;
        var reportQuestion = reportProblem.getReportQuestions().stream()
                .filter(rq -> rq.getQuestionId() == mistake.getQuestionId())
                .findFirst().orElse(null);
        if (Objects.isNull(reportQuestion)) return null;
        var reportProblemVO = TaskReportAssembler.Instance.toReportProblemVO(reportProblem);
        var reportQuestionVO = TaskReportAssembler.Instance.toReportQuestionVO(reportQuestion);
        var mistakeQuestionDto = MistakeAssembler.Instance.toMistakeQuestionDto(mistake);
        mistakeQuestionDto.setReportProblem(reportProblemVO);
        mistakeQuestionDto.setReportQuestion(reportQuestionVO);
        return mistakeQuestionDto;
    }

    @HandleServiceException
    public MistakeVO createMistake(@NotNull CreateMistakeCommand command) {
        command.setOperatorId(getCurrentUserId());

        var existedMistake = mistakeRepository.getMistake(command.getUserId(), command.getTaskId(), command.getSetId(), command.getProblemId(),
                command.getQuestionId());
        if (Objects.nonNull(existedMistake)) {
            return MistakeAssembler.Instance.toMistakeVO(existedMistake);
        }

        var mistake = mistakeService.createMistake(command.getUserId(), command.getTaskId(), command.getSetId(), command.getProblemId(), command.getQuestionId());
        if (Objects.isNull(mistake)) return null;
        return MistakeAssembler.Instance.toMistakeVO(mistake);
    }


    @HandleServiceException
    public boolean createMistakesByReport(@NotNull CreateMistakesByReportCommand command) {
        command.setOperatorId(getCurrentUserId());
        var taskId = command.getTaskId();
        var report = taskReportService.getReportByTask(taskId);
        if (report == null) return false;
        var task = taskRepository.getTask(taskId);
        if (task == null) {
            var errorCode = "service-exception.distribution-task.id.notfound";
            throw new ServiceException(null, errorCode, "distribution task id: " + taskId);
        }
        var sheet = sheetRepository.getSheetByTask(taskId);
        if (sheet == null) {
            var errorCode = "service-exception.sheet.notfound";
            throw new ServiceException(null, errorCode,
                    "task id / set id: " + task.getUserId() + " / " + task.getSetId());
        }
        var reportProblems = report.getReportProblems();
        // 练习任务报告中的错题全部加入错题本
        for (var reportProblem : reportProblems) {

            for (var reportQuestion : reportProblem.getReportQuestions()) {
                if (reportQuestion.getCorrectStatus() != 1) {
                    // 记录错题
                    var mistake = new Mistake();
                    mistake.setProblemId(reportQuestion.getProblemId());
                    mistake.setQuestionId(reportQuestion.getQuestionId());
                    mistake.setSetId(task.getSetId());
                    mistake.setSheetId(sheet.getSheetId());
                    long detailId = sheetRepository.getSheetDetailId(sheet.getDetails(), reportProblem.getProblemId(),
                            reportQuestion.getQuestionId());
                    mistake.setSheetDetailId(detailId);
                    mistake.setTaskId(taskId);
                    mistake.setUserId(sheet.getUserId());
                    mistake.setRecordTime(LocalDateTime.now());
                    mistakeService.createMistake(mistake, command.getOperatorId());
                }
            }
        }
        return true;
    }

    @HandleServiceException
    public boolean deleteMistake(@NotNull DeleteCommand command) {
        command.setOperatorId(getCurrentUserId());
        return AppServiceUtils.delete(command.getId(), command.getOperatorId(), mistakeService::deleteMistake);
    }
}
